package com.example.demo.websoket.handler;

import com.alibaba.fastjson.JSON;
import com.example.demo.websoket.config.RabbitMqConfig;
import com.example.demo.websoket.entity.WsMessage;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class WebSocketHandlerImpl extends TextWebSocketHandler {

    private static final Logger logger = LoggerFactory.getLogger(WebSocketHandlerImpl.class);
    //Map<org,Map<UserCode,WebSocketSession>>
    public final static Map<String, Map<String, WebSocketSession>> departUsers;
    //Map<userCode,departMent>
    public final static Map<String, String> userDepart;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    static {
        departUsers = new ConcurrentHashMap<>();
        userDepart = new ConcurrentHashMap<>();
    }

    /**
     * 将消息发送至MQ
     */
    public void sendMsgToMq(WsMessage wsMessage) {
        rabbitTemplate.convertAndSend(RabbitMqConfig.WEBSOCKET_MQ_EXCHANGE, "", JSON.toJSONString(wsMessage));
    }

    /**
     * 监听获取MQ的消息发送至websocket
     */
    @RabbitListener(queues = "#{websocketQueue.name}")
    public void websocketMqlistener(String message) {
        try {
            logger.info("从mq获取到消息:{}", message);
            WsMessage wsMessage = JSON.parseObject(message, WsMessage.class);
            sendMsgToWebScoket(wsMessage);
        } catch (Exception e) {
            logger.error("消息发送失败");
        }
    }

    /**
     * 向不同接收者发消息统一入口
     */
    public void sendMsgToWebScoket(WsMessage wsMessage) throws IOException {
        switch (wsMessage.getReceiverType()) {
            case WsMessage.RECEIVER_TYPE_ORG:
                sendToDepartMent(wsMessage, null);
                break;
            case WsMessage.RECEIVER_TYPE_PERSON:
                sendTOUser(wsMessage);
                break;
            case WsMessage.RECEIVER_TYPE_ALL:
                sendMessageToAll(wsMessage);
                break;
            default:
                logger.info("消息类型为空,发送失败！");
        }

    }

    /**
     * 推送消息给所有用户
     *
     * @return
     * @throws: IOException
     * @param: message
     */
    public boolean sendMessageToAll(WsMessage wsMessage) throws IOException {
        logger.info("广播消息给所有用户");
        //发送给所有组织下的所有用户
        for (String departMent : departUsers.keySet()) {
            sendToDepartMent(wsMessage, departMent);
        }
        return true;
    }

    /**
     * 根据科室发送消息
     */
    public void sendToDepartMent(WsMessage wsMessage, String receiver) throws IOException {
        if (StringUtils.isEmpty(receiver)) {
            receiver = wsMessage.getReceOrgCode();
        }
        if (Strings.isEmpty(receiver)) return;

        Map<String, WebSocketSession> usersSessionMap = departUsers.get(receiver);
        if (usersSessionMap == null) return;
        for (String userCode : usersSessionMap.keySet()) {
            try {
                WebSocketSession session = usersSessionMap.get(userCode);
                sendMsg(session, wsMessage);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 发送消息给个人
     */
    public void sendTOUser(WsMessage wsMessage) throws IOException {
        WebSocketSession session = departUsers.get(wsMessage.getReceOrgCode()).get(wsMessage.getRecePersonCode());
        sendMsg(session, wsMessage);
    }

    /**
     * 发送消息
     */
    public void sendMsg(WebSocketSession session, WsMessage message) throws IOException {
        session.sendMessage(new TextMessage(JSON.toJSONString(message)));
    }

    /**
     * 接收消息
     */
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        String msgStr = message.getPayload().toString();
        logger.info("接收到消息{}",msgStr);
        sendMsgToMq(JSON.parseObject(msgStr,WsMessage.class));
    }

    /**
     * 第一次登陆,记录session
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        Map<String, Object> map = session.getAttributes();
        String userCode = (String) map.get("userCode");
        String departMent = (String) map.get("departMent");

        userDepart.put(userCode, departMent);
        Map<String, WebSocketSession> userSession = departUsers.get(departMent);
        if (userSession == null) {
            userSession = new ConcurrentHashMap<>();
        }
        userSession.put(userCode, session);
        departUsers.put(departMent, userSession);
    }

    /**
     * 连接关闭时移除会话信息
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        removeSession(session);
    }

    /**
     * 处理错误
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        logger.error("websocket error", exception);
        removeSession(session);
    }

    /**
     * 移除会话信息
     *
     * @param session
     */
    private void removeSession(WebSocketSession session) {
        Map<String, Object> map = session.getAttributes();
        String userCode = map.get("userCode").toString();
        String departMent = map.get("departMent").toString();
        departUsers.get(departMent).remove(userCode);
        userDepart.remove(userCode);
        if (session.isOpen()) {
            try {
                session.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
